package com.hjx.movie.core.security.auth;

import com.fasterxml.jackson.databind.ObjectMapper;


import com.hjx.movie.core.security.util.JwtAuthTokenUtil;
import com.hjx.movie.common.utils.Result;
import com.hjx.movie.common.utils.ResultType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * <p>Title:</p>
 * <p>Description:</p>
 *
 * @author hjx
 * @date 2020/6/10 - 9:06
 */
@Slf4j
@Component
public class JwtAuthTokenFilter extends OncePerRequestFilter {
    @Resource
    private JwtAuthTokenUtil jwtAuthTokenUtil;
    @Resource
    private AuthUserDetailsService authUserDetailsService;
    @Resource
    private RedisTemplate<String, String> redisTemplate;


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        log.info("进入TOKEN过滤器");
        String jwtToken = request.getHeader(jwtAuthTokenUtil.getHeader());
        if (StringUtils.isBlank(jwtToken)){
            jwtToken = request.getParameter("token");
        }
        log.info("前端传入的TOKEN信息为:{}",jwtToken);

        if (StringUtils.isNotBlank(jwtToken)) {
            ObjectMapper mapper = new ObjectMapper();
            //Token信息不为空则进入认证流程
            //从token中提取用户名
            String uid = jwtAuthTokenUtil.getUidFromToken(jwtToken);
            log.info("从TOKEN中解析出的UID为:{}",uid);

            if (uid != null &&
                    SecurityContextHolder.getContext().getAuthentication() == null) {

                //去redis中寻找该用户当前所对应的token
                String redisToken = redisTemplate.opsForValue().get(jwtAuthTokenUtil.getCacheTokenName(uid));
                log.debug("从缓存中获取到的Token:{}",redisToken);
                //如果redis中能找到到该用户所对应的token
                //并且对比redis中的token和当前请求头所带上来的token是一致的 那就证明该用户使用的是最新颁发的令牌进来的 通过验证
                //否则返回token信息过期响应
                if (redisToken != null && redisToken.equals(jwtToken)) {
                    //加载权限
                    UserDetails userDetails = authUserDetailsService.loadUserByUsername(uid);
                    //验证用户名和 令牌是否过期
                    if (jwtAuthTokenUtil.verifierToken(jwtToken, userDetails)) {
                        //给使用该jwt令牌的用户进行授权
                        UsernamePasswordAuthenticationToken upToken =
                                new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                        //交给spring security进行管理
                        SecurityContextHolder.getContext().setAuthentication(upToken);
                        log.info("TOKEN验证通过");
                    } else {
                        log.info("TOKEN验证失败");
                        response.setHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE);
                        response.getWriter().write(mapper.writeValueAsString(Result.error(ResultType.TOKEN_ERROR,"TOKEN验证失败")));
                        return;
                    }
                } else {
                    log.info("TOKEN与Redis中的Token不匹配");
                    response.setHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE);
                    response.getWriter().write(mapper.writeValueAsString(Result.error(ResultType.TOKEN_ERROR,"TOKEN信息过期")));
                    return;
                }
            } else {
                log.info("TOKEN解析失败");
                response.setHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE);
                response.getWriter().write(mapper.writeValueAsString(Result.error(ResultType.TOKEN_ERROR,"TOKEN解析失败")));
                return;
            }
        }
        //若没经过认证流程直接通过 如果访问的是配置中放行的接口可直接通过
        //否则就会被SpringSecurity拦截下来 并且返回403
        filterChain.doFilter(request, response);
    }
}
